#include "qe_log.h"
#include "qe_string.h"
#include "qe_earlycon.h"
#include "libfdt.h"



QELOG_DOMAIN(QELOG_DOMAIN_EARLYCON);

QE_EARLYCON_START(start);
QE_EARLYCON_END(end);

static qe_earlycon_entry *earlycon_tab = QE_NULL;
static qe_ubase active_iobase = 0;
static qe_earlycon_entry *active_entry = QE_NULL;
static qe_size earlycon_tabsize = 0;



void qe_earlycon_init(const void *fdt)
{
    int i;
    int len;
    int offset;
    int chosen_offset;
    const char *stdout_path;
    const char *compatible;
    const qe_u32 *prop_reg;

    #if defined(__CC_ARM) || (defined(__ARMCC_VERSION) && __ARMCC_VERSION >= 6000000)
        earlycon_tab = (qe_earlycon_entry *)&qe_earlycon$$base;
        earlycon_tabsize = ((qe_size)(&qe_earlycon$$limit)
                                - (qe_size)(&qe_earlycon$$base))
                                / sizeof(qe_earlycon_entry);
    #elif defined(__ICCARM__) || defined(__ICCRX__)
        earlycon_tab = (qe_earlycon_entry *)(__section_begin("qe_earlycon"));
        earlycon_tabsize = ((qe_size)(__section_end("qe_earlycon"))
                                - (qe_size)(__section_begin("qe_earlycon")))
                                / sizeof(qe_earlycon_entry);
    #elif defined(__GNUC__)
        earlycon_tab = (qe_earlycon_entry *)(&qe_earlycon_start);
        earlycon_tabsize = ((qe_size)(&qe_earlycon_end)
                                - (qe_size)(&qe_earlycon_start))
                                / sizeof(qe_earlycon_entry);
    #else
        #error not supported compiler, please use earlycon table mode
    #endif

    chosen_offset = fdt_path_offset(fdt, "/chosen");
    if (chosen_offset < 0)
        return;

    stdout_path = fdt_getprop(fdt, chosen_offset, "stdout-path", &len);
    if (!stdout_path)
        return;

    offset = fdt_path_offset(fdt, stdout_path);
    if (offset < 0)
        return;

    compatible = fdt_getprop(fdt, offset, "compatible", &len);
    if (!compatible)
        return;

    prop_reg = fdt_getprop(fdt, offset, "reg", &len);
    if (!prop_reg)
        return;

    qe_debug("chosen %s");

    for (i=0; i<earlycon_tabsize; i++) {
        if (qe_strcmp(compatible, earlycon_tab[i].compatible) == 0) {
            earlycon_tab[i].init(fdt, offset);
            active_entry = &earlycon_tab[i];
            active_iobase = (qe_u32)fdt32_to_cpu(*prop_reg);
            return;
        }
    }
}

void qe_earlycon_putc(char c)
{
    if (active_entry != QE_NULL)
        active_entry->putc(active_iobase, c);
}